--[[

    Filename:	auxsck.lua

    Programmer:	aesiot

    Remark:

--]]

local Socket = require( "Socket" )
local auxlib = require( "auxlib" )

local auxsck = { _VERSION = "1.0" }

--------------------------------------------------------------------------------

local function new_with_object( object )

	local result = {}

	local metatable = {

		Object = object,

		__index = {},

		__metatable = "cannot get a protected metatable"
	}

	function object.on_close()

		auxlib.pcall( result.on_close, result )

		metatable.Object.on_close = nil
		metatable.Object.on_read  = nil
		metatable.Object.on_ready = nil

		metatable.Object = nil

		metatable = nil
	end

	function object.on_destroy()

		auxlib.pcall( result.on_destroy, result )

		result = nil
	end

	function object.on_read( data )

		auxlib.pcall( result.on_read, result, data )
	end

	function object.on_ready()

		auxlib.pcall( result.on_ready, result )
	end

	function metatable.__index:close()

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:close() )

		return self
	end

	function metatable.__index:getpeername()

		auxlib.assert( self == result )

		return metatable.Object:getpeername()
	end

	function metatable.__index:getsockname()

		auxlib.assert( self == result )

		return metatable.Object:getsockname()
	end

	function metatable.__index:send( data )

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:send( data ) )

		return self
	end

	setmetatable( result, metatable )

	return result
end

function auxsck.new_tcp_client( host, port )

	local r = auxlib.assert( Socket.new_tcp_client() )

	auxlib.assert( r:connect( host, port ) )

	return new_with_object( r )
end

function auxsck.new_tls_client( host, port, hostname, cert, key, ca )

	local r = auxlib.assert( Socket.new_tls_client( host, port, hostname, cert, key, ca ) )

	return new_with_object( r )
end

function auxsck.new_udp_client( host, port )

	local r = auxlib.assert( Socket.new_udp_client() )

	auxlib.assert( r:connect( host, port ) )

	return new_with_object( r )
end

--------------------------------------------------------------------------------

function auxsck.new_tcp_server( addr, backlog, cert, key, ca )

	local object = nil

	if cert and key then

		object = auxlib.assert( Socket.new_tls_server( cert, key, ca ) )
	else

		object = auxlib.assert( Socket.new_tcp_server() )
	end

	auxlib.assert( object:listen( addr, backlog ) )

	local result = {}

	local metatable = {

		Object = object,

		__index = {},

		__metatable = "cannot get a protected metatable"
	}

	function object.on_accept( client )

		if not auxlib.pcall( result.on_accept, result, new_with_object( client ) ) then

			client:close()
		end
	end

	function object.on_close()

		auxlib.pcall( result.on_close, result )

		metatable.Object.on_accept = nil
		metatable.Object.on_close  = nil
		metatable.Object.on_ready  = nil

		metatable.Object = nil

		metatable = nil
	end

	function object.on_destroy()

		auxlib.pcall( result.on_destroy, result )

		result = nil
	end

	function object.on_ready()

		auxlib.pcall( result.on_ready, result )
	end

	function metatable.__index:close()

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:close() )

		return self
	end

	setmetatable( result, metatable )

	return result
end

--------------------------------------------------------------------------------

function auxsck.new_udp_object( addr )

	local object = auxlib.assert( Socket.new_udp_object() )

	auxlib.assert( object:create( addr ) )

	local result = {}

	local metatable = {

		Object = object,

		__index = {},

		__metatable = "cannot get a protected metatable"
	}

	function object.on_close()

		auxlib.pcall( result.on_close, result )

		metatable.Object.on_close = nil
		metatable.Object.on_read  = nil
		metatable.Object.on_ready = nil

		metatable.Object = nil

		metatable = nil
	end

	function object.on_destroy()

		auxlib.pcall( result.on_destroy, result )

		result = nil
	end

	function object.on_read( data, addr )

		auxlib.pcall( result.on_read, result, data, addr )
	end

	function object.on_ready()

		auxlib.pcall( result.on_ready, result )
	end

	function metatable.__index:broadcast( port, data )

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:broadcast( port, data ) )

		return self
	end

	function metatable.__index:close()

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:close() )

		return self
	end

	function metatable.__index:getsockname()

		auxlib.assert( self == result )

		return metatable.Object:getsockname()
	end

	function metatable.__index:multicast_add( addr, loop )

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:multicast_add( addr, loop ) )

		return self
	end

	function metatable.__index:send( addr, data )

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:send( addr, data ) )

		return self
	end

	function metatable.__index:set_broadcast( value )

		auxlib.assert( self == result )

		auxlib.assert( metatable.Object:set_broadcast( value ) )

		return self
	end

	setmetatable( result, metatable )

	return result
end

--------------------------------------------------------------------------------

return auxsck
